const express = require('express');
const router = express.Router();
const fs = require('fs');
const path = require('path');
const dataModelError = require('../../lib/module/error/datamodel');
const { routeWrapper, makeErrorReturn } = require('../../lib/module/tool/route');
const Schema = require('../../lib/entity/schema');

const modelPathMap = {};

function listModel (baseDir) {
    const fileList = fs.readdirSync(baseDir, 'utf-8');
    for (let i = 0; i < fileList.length; i++) {
        const stat = fs.lstatSync(baseDir + '/' + fileList[i]);
        // 是目录，需要继续
        if (stat.isDirectory()) {
            listModel(baseDir + '/' + fileList[i]);
        } else {
            modelPathMap[fileList[i].split('.')[0]] = baseDir + '/' + fileList[i];
        }
    }
}

listModel(path.join(__dirname, '../../lib/entity'));

router.use('/:modelName', routeWrapper(async (req, res, next) => {
    const modelName = req.params.modelName;
    if (!modelPathMap[modelName]) {
        res.send(makeErrorReturn(new dataModelError.DataModelNotExist('没有该数据模型')));
    }
    req.Model = require(modelPathMap[modelName]);
    next();
}));

// insertRecord
router.post('/:modelName/insertRecord', routeWrapper(async (req) => {
    const Model = new req.Model();
    const result = await Model.getDoc({ ...req.body }).save();
    if (!result) {
        throw new dataModelError.RecordInsertFail('插入数据失败!');
    }
    return result;
}, true));

// deleteById
router.post('/:modelName/deleteById/:dataId', routeWrapper(async (req) => {
    const Model = new req.Model();
    const result = await Model.getModel().deleteOne({ _id: req.params.dataId });
    if (!result) {
        throw new dataModelError.RecordDeleteFail();
    }
    return result;
}, true));

// updateById
router.post('/:modelName/updateById/:dataId', routeWrapper(async (req) => {
    const Model = new req.Model();
    const result = await Model.getModel().updateOne({ _id: req.params.dataId }, req.body);
    if (!result) {
        throw new dataModelError.RecordUpdateFail();
    }
    return result;
}, true));

// queryById
router.get('/:modelName/queryById/:dataId', routeWrapper(async (req, res, next) => {
    const Model = new req.Model();
    const result = await Model.getModel().findById(req.params.dataId);
    if (!result) {
        throw new dataModelError.RecordNotExist();
    }
    return result;
}, false));

// queryByCondition
router.post('/:modelName/queryByCondition/:offset/:rows', routeWrapper(async (req) => {
    const offset = parseInt(req.params.offset);
    const rows = parseInt(req.params.rows);
    const Model = new req.Model();

    const query = req.body;
    Object.keys(query).forEach(key => {
        if (key.indexOf('ObjectId') !== -1) {
            const realKey = key.split('_')[0];
            query[realKey] = Schema.StringToObjectId(query[key]);
            delete query[key];
        }
    });

    let documentList;
    if (query.sort) {
        const sort = query.sort;
        delete query.sort;
        documentList = await Model.getModel().find(query).sort(sort).skip(offset).limit(rows);
    } else {
        documentList = await Model.getModel().find(query).skip(offset).limit(rows);
    }
    const documentCount = await Model.getModel().countDocuments(query);

    return {
        documentCount,
        documentList
    };
}, true));

// updateManyByCondition
router.post('/:modelName/updateManyByCondition', routeWrapper(async (req) => {
    const Model = new req.Model();
    return await Model.getModel().updateMany(req.body.query, req.body.fields);
}, true));

// countByCondition
router.post('/:modelName/countByCondition', routeWrapper(async (req, res, next) => {
    const Model = new req.Model();
    return Model.getModel().countDocuments(req.body);
}, true));

router.get('/:modelName/count', routeWrapper(async (req, res, next) => {
    const Model = new req.Model();
    return Model.getModel().estimatedDocumentCount();
}, true));

module.exports = {
    basePath: '/data',
    router
};
